home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-05
/
drivers1.zip
/
8390.ASM
< prev
next >
Wrap
Assembly Source File
|
1992-01-13
|
34KB
|
1,220 lines
;History:640,1
dp8390_version equ 1 ;version number of the generic 8390 driver.
; Copyright, 1988-1992, Russell Nelson, Crynwr Software
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, version 1.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
; This driver is the work of several people: Bob Clements, Eric Henderson,
; Dave Horne, Glenn Talbott and Russell Nelson.
pause_ macro
; jmp $+2
;
; The reason for the pause_ macro is to establish a minimum time between
; accesses to the card hardware. The assumption is that the fetch and execution
; of the jmp $+2 instruction will provide this time. In a fast cache machine
; this may be a false assumption. In a fast cache machine, there may be
; NO REAL TIME DIFFERENCE between the two I/O instruction streams below:
;
; in al,dx in al,dx
; jmp $+2
; in al,dx in al,dx
;
; To establish a minimum delay, an I/O instruction must be used. A good rule of
; thumb is that ISA I/O instructions take ~1.0 microseconds and MCA I/O
; instructions take ~0.5 microseconds. Reading the NMI Status Register (0x61)
; is a good way to pause on all machines.
;
; The National 8390 Chip (NIC) requires 4 bus clocks between successive
; chip selects (National DP8390 Data Sheet Addendum, June 1990 -- it took them
; long enough to figure this out and tell everyone) or the NIC behaves badly.
; Therefor one I/O instruction should be inserted between each successive
; NIC I/O instruction that could occur 'back - to - back' on a fast cache
; machine.
; - gft - 910529
;
push ax
in ax, 61h
pop ax
;
endm
;
; The longpause macro was originally written as this:
;
;longpause macro
; push cx
; mov cx,0
; loop $
; pop cx
;endm
;
; It was only used to stall while hard resetting the card. On my
; 25Mhz 486 longpause was taking more than 18ms, and almost forever
; on slower machines, much longer than necessary and not predictable.
;
; To be able to utilize longpause elsewhere and make it machine independent and
; predictable, I have re-written it to be a fixed time of 1.6ms, which just
; happens to be the time National recommends waiting for the NIC chip to
; stop sending or receiving after being commanded to stop.
;
; Based on the assumption that ISA specs mandate a 1.0 uS minimum I/O cycle
; Microchannel a 0.5uS minimum I/O cycle, and the NMI Status register (location
; 61h) is readable via I/O cycle on all machines, the longpause macro is now
; defined below. - gft - 901604
; (I realize that on slow machines this may take much longer, but the point
; is that on FAST machines it should never be faster than 1.6ms)
longpause macro
local lp_not_mc
push cx
push ax
mov cx,1600 ; 1.6ms = 1600*1.0us
test sys_features,MICROCHANNEL
je lp_not_mc
shl cx,1 ; twice as many loops for Microchannel
lp_not_mc:
in al,61h
loop lp_not_mc
pop ax
pop cx
endm
extrn sys_features: byte
sm_rstop_ptr db SM_RSTOP_PG
rxcr_bits db ENRXCR_BCST ; Default to ours plus multicast
public curr_hw_addr, mcast_list_bits, mcast_all_flag
curr_hw_addr db 0,0,0,0,0,0 ;Address set into the 8390
mcast_list_bits db 0,0,0,0,0,0,0,0 ;Bit mask from last set_multicast_list
mcast_all_flag db 0 ;Non-zero if hware should have all
; ones in mask rather than this list.
mcast_sw_filter db 0 ; set if software filter is required.
mcast_sw_fin dw 0
mcast_sw_fout dw 0
public rcv_modes
rcv_modes dw 7 ;number of receive modes in our table.
dw 0 ;There is no mode zero
dw rcv_mode_1
dw rcv_mode_2
dw rcv_mode_3
dw rcv_mode_4
dw rcv_mode_5
dw rcv_mode_6
public mcast_tab
mcast_hcount dw 0 ; multicast header count
mcast_tab_b db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh ; entry for broadcast
mcast_tab db (MAX_MULTICAST*EADDR_LEN) dup (0)
;
; a temp buffer for the received header
;
RCV_HDR_SIZE equ 26 ; 2 ids @6 + protocol @2+8, + header @4
rcv_hdr db RCV_HDR_SIZE dup(0)
;
; The board data
;
public board_data
BOARD_DATA_SIZE equ 32
board_data db BOARD_DATA_SIZE dup(0)
soft_tx_errors dw 0,0
soft_tx_err_bits db 0
soft_rx_errors dw 0,0
soft_rx_err_bits db 0
ifdef debug ; Include a very useful logging mechanism.
; The log entry structure. Log entries include useful data such as
; a type (each place a log entry is made uses a different type), various
; chip status, ring buffer status, log entry dependent data, and optionally
; 8259 interrupt controller status.
logentry struc
le_type db 0 ; Log entry type
le_ccmd db ? ; Value of CCMD register
le_isr db ? ; Value of ISR register
le_tsr db ? ; Value of TSR register
le_tcur dw ? ; Value of sm_tcur
le_tboundary dw ? ; Value of sm_tboundary
le_tnum dw ? ; Value of sm_tnum
le_dw dw ? ; Log type specific dw data
ifndef mkle8259 ; Log 8259 status?
le_dd dd ? ; Log type specific dd data
else
le_irr1 db ? ; Value of 8259-1 IRR register
le_isr1 db ? ; Value of 8259-1 ISR register
le_irr2 db ? ; Value of 8259-2 IRR register
le_isr2 db ? ; Value of 8259-2 ISR register
endif
logentry ends
; The types of log entries.
LE_SP_E equ 0 ; send_pkt entry
LE_SP_X equ 1 ; send_pkt exit
LE_ASP_E equ 2 ; as_send_pkt entry
LE_ASP_X equ 3 ; as_send_pkt exit
LE_RBALLOC_E equ 4 ; tx_rballoc entry
LE_RBALLOC_X equ 5 ; tx_rballoc exit
LE_COPY_E equ 6 ; sm_copy entry
LE_COPY_X equ 7 ; sm_copy exit
LE_START_E equ 8 ; tx_start entry
LE_START_X equ 9 ; tx_start exit
LE_XMIT_E equ 0ah ; xmit entry
LE_XMIT_X equ 0bh ; xmit exit
LE_TXISR_E equ 0ch ; txisr entry
LE_TXISR_X equ 0dh ; txisr exit
LE_RECV_E equ 0eh ; recv entry
LE_RECV_X equ 0fh ; recv exit
LE_RCVFRM_E equ 10h ; rcv_frm entry
LE_RCVFRM_X equ 11h ; rcv_frm exit
LE_COPY_L equ 12h ; sm_copy loop
LE_TIMER_E equ 13h ; timer entry
LE_TIMER_X equ 14h ; timer exit
public log, log_index
log logentry 256 dup (<>) ; The log itself
log_index db 0 ; Index to current log entry
; The macro used to create log entries.
mkle macro letype, ledw, ledd, ledd2 ; Make an entry in the log
pushf ; Save interrupt enable state
cli ; Disable interrupts
push dx ; Save registers
push bx
push ax
mov bl, log_index ; Get current log_index
xor bh, bh ; Clear high byte
shl bx, 1 ; Multiply by sixteen
shl bx, 1
shl bx, 1
shl bx, 1
mov log[bx].le_type, letype ; Store log entry type
loadport ; Base of device
setport EN_CCMD ; Point at chip command register
in al, dx ; Get chip command state
mov log[bx].le_ccmd, al ; Store CCMD value
setport EN0_ISR ; Point at chip command register
in al, dx ; Get chip command state
mov log[bx].le_isr, al ; Store ISR value
setport EN0_TSR ; Point at chip command register
in al, dx ; Get chip command state
mov log[bx].le_tsr, al ; Store TSR value
mov ax, sm_tcur ; Get current sm_tcur
mov log[bx].le_tcur, ax ; Store sm_tcur value
mov ax, sm_tboundary ; Get current sm_tboundary
mov log[bx].le_tboundary, ax ; Store sm_tboundary value
mov ax, sm_tnum ; Get current sm_tnum
mov log[bx].le_tnum, ax ; Store sm_tnum value
mov log[bx].le_dw, ledw ; Store log entry dw
ifndef mkle8259 ; Include extra per-type data
mov word ptr log[bx].le_dd, ledd ; Store low word of log entry dd
mov word ptr log[bx].le_dd+2, ledd2 ; Store high word of log entry dd
else ; Include 8259 status
mov al,0ah ; read request register from
out 0a0h,al ; secondary 8259
pause_
in al,0a0h ; get it
mov log[bx].le_irr2, al
mov al,0bh ; read in-service register from
out 0a0h,al ; secondary 8259
pause_
in al,0a0h ; get it
mov log[bx].le_isr2, al
mov al,0ah ; read request register from
out 020h,al ; primary 8259
pause_
in al,020h ; get it
mov log[bx].le_irr1, al
mov al,0bh ; read in-service register from
out 020h,al ; primary 8259
pause_
in al,020h ; get it
mov log[bx].le_isr1, al
endif
ifdef screenlog ; Log the entry type to the screen too
push es
mov ax, 0b800h ; Color screen only...
mov es, ax
mov bl, log_index ; Get current log_index
xor bh, bh ; Clear high byte
shl bx, 1 ; Multiply by sixteen
add bx, 3360
mov byte ptr es:[bx-1], 07h
mov byte ptr es:[bx], letype+30h
mov byte ptr es:[bx+1], 70h
pop es
endif
inc log_index ;
pop ax ; Restore registers
pop bx
pop dx
popf ; Restore interrupt enable state
endm
else
mkle macro letype, ledw, ledd, ledd2 ; Define an empty macro
endm
endif
public as_send_pkt
; The Asynchronous Transmit Packet routine.
; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
; interrupts possibly enabled.
; Exit with nc if ok, or else cy if error, dh set to error number.
; es:di and interrupt enable flag preserved on exit.
as_send_pkt:
ret
public drop_pkt
; Drop a packet from the queue.
; Enter with es:di -> iocb.
drop_pkt:
assume ds:nothing
ret
public xmit
; Process a transmit interrupt with the least possible latency to achieve
; back-to-back packet transmissions.
; May only use ax and dx.
xmit:
assume ds:nothing
ret
public send_pkt
send_pkt:
;enter with ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
assume ds:nothing
mkle LE_SP_E, cx, si, ds
;ne1000 checks the packet size at this point, which is probably more sensible.
loadport ; Point at chip command register
setport EN_CCMD ; ..
pause_
;ne1000 fails to check to see if the transmitter is still busy.
mov bx, 8000h ; Avoid infinite loop
tx_wait:
in al, dx ; Get chip command state
test al,ENC_TRANS ; Is transmitter still running?
jz tx_idle ; Go if free
dec bx ; Count the timeout
jnz tx_wait ; Fall thru if TX is stuck
call count_out_err ; Should count these error timeouts
; Maybe need to add recovery logic here
tx_idle:
cmp cx,GIANT ; Is this packet too large?
ja send_pkt_toobig
cmp cx, RUNT ; Is the frame long enough?
jnb tx_oklen ; Go if OK
mov cx, RUNT ; Stretch frame to minimum allowed
tx_oklen:
push cx ; Hold count for later
loadport ; Set up for address
setport EN0_ISR
pause_
mov al,ENISR_RDC ; clear remote interrupt int.
out dx,al
setport EN0_TCNTLO ; Low byte of TX count
pause_
mov al, cl ; Get the count
out dx, al ; Tell card the count
setport EN0_TCNTHI ; High byte of TX count
pause_
mov al, ch ; Get the count
out dx, al ; Tell card the count
xor ax, ax ; Set up ax at base of tx buffer
mov ah, SM_TSTART_PG ; Where to put tx frame
pop cx ; Get back count to give to board
call block_output
jc tx_no_rdc
loadport
setport EN0_TPSR ; Transmit Page Start Register
pause_
mov al, SM_TSTART_PG
out dx, al ; Start the transmitter
setport EN_CCMD ; Chip command reg
pause_
mov al, ENC_TRANS+ENC_NODMA+ENC_START
out dx, al ; Start the transmitter
mkle LE_SP_X, cx, 1, 0
clc ; Successfully started
sti
ret ; End of transmit-start routine
send_pkt_toobig:
mov dh,NO_SPACE
stc
sti
ret
tx_no_rdc:
mov dh,CANT_SEND
mkle LE_SP_X, cx, 0, 0
stc
sti
ret
count_soft_err:
add word ptr soft_tx_errors,1
adc word ptr soft_tx_errors+2,0
or byte ptr soft_tx_err_bits,al
ret
public get_address
get_address:
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
; Give caller the one currently in the 8390, not necessarily the one in PROM.
assume ds:code
cmp cx, EADDR_LEN ; Caller wants a reasonable length?
jb get_addr_x ; No, fail.
mov cx, EADDR_LEN ; Move one ethernet address from our copy
mov si, offset curr_hw_addr ; Copy from most recent setting
rep movsb
mov cx, EADDR_LEN ; Tell caller how many bytes we fed him
clc ; Carry off says success
ret
get_addr_x:
stc ; Tell caller our addr is too big for him
ret
public set_address
set_address:
assume ds:nothing
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
;
cmp cx,EADDR_LEN ;ensure that their address is okay.
je set_address_4
mov dh,BAD_ADDRESS
stc
jmp short set_address_done
set_address_4:
push cs ; Copy from them to our RAM copy
pop es ; Destination of move
mov di, offset curr_hw_addr
rep movsb ; Move their address
call set_8390_eaddr ; Put that address in the chip
set_address_okay:
mov cx,EADDR_LEN ;return their address length.
clc
set_address_done:
push cs
pop ds
assume ds:code
ret
; Copy our Ethernet address from curr_hw_addr into the DS8390
set_8390_eaddr:
cld
push cs ; Get it from our local RAM copy
pop ds
mov si, offset curr_hw_addr
mov cx, EADDR_LEN ; Move one ethernet address from our copy
loadport
setport EN_CCMD ; Chip command register
pause_
cli ; Protect from irq changing page bits
mov al, ENC_NODMA+ENC_PAGE1 ;+ENC_STOP
out dx, al ; Switch to page one for writing eaddr
setport EN1_PHYS ; Where it goes in 8390
set_8390_1:
lodsb
out dx,al
pause_
inc dx
loop set_8390_1
loadport
setport EN_CCMD ; Chip command register
pause_
mov al, ENC_NODMA+ENC_PAGE0 ;+ENC_STOP
out dx, al ; Restore to page zero
sti ; OK for interrupts now
ret
; Routines to set address filtering modes in the DS8390
rcv_mode_1: ; Turn off receiver
mov al, ENRXCR_MON ; Set to monitor for counts but accept none
jmp short rcv_mode_set
rcv_mode_2: ; Receive only packets to this interface
mov al, 0 ; Set for only our packets
jmp short rcv_mode_set
rcv_mode_3: ; Mode 2 plus broadcast packets (This is the default)
mov al, ENRXCR_BCST ; Set four ours plus broadcasts
jmp short rcv_mode_set
rcv_mode_4: ; Mode 3 plus selected multicast packets
mov al, ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
mov mcast_all_flag,0 ; need to do sw filter.
mov mcast_sw_filter,1 ; because chip filter is not 100%
jmp short rcv_mode_set
rcv_mode_5: ; Mode 3 plus ALL multicast packets
mov al, ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
mov mcast_all_flag,1
jmp short rcv_mode_set
rcv_mode_6: ; Receive all packets (Promiscuous physical plus all multi)
mov al, ENRXCR_BCST+ENRXCR_MULTI+ENRXCR_PROMP
mov mcast_all_flag,1
rcv_mode_set:
push ax ; Hold mode until masks are right
call set_8390_multi ; Set the multicast mask bits in chip
pop ax
loadport
setport EN0_RXCR ; Set receiver to selected mode
pause_
out dx, al
mov rxcr_bits,al ; Save a copy of what we set it to
ret
public set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, cx = number of addresses.
;return nc if we set all of them, or cy,dh=error if we didn't.
assume ds:nothing
push cs
pop es ; set es to destination
mov di,offset mcast_tab
mov ax,cx ; save byte count
repz movsb
mov dx,0
mov cx,6
div cx
mov mcast_hcount,ax
;
mov word ptr mcast_list_bits,0
mov word ptr mcast_list_bits+2,0
mov word ptr mcast_list_bits+4,0
mov word ptr mcast_list_bits+6,0
;
mov cx,mcast_hcount
inc cx
mov di,offset mcast_tab_b
set_mcl_1:
call add_mc_bits
add di,6
loop set_mcl_1
call set_8390_multi ; Set the multicast mask bits in chip
clc
mov dh,0
ret
;
; multicast is at es:di
assume ds:nothing
add_mc_bits:
push cx
push di
mov cx,6
mov dx,0ffffh ; this is msw.
mov bx,0ffffh ; set 32 bit number
add_mcb_1:
mov al,es:[di]
inc di
call upd_crc ; update crc
loop add_mcb_1 ; and loop.
mov ah,0
mov al,dh ; get ms 8 bits,
rol al,1
rol al,1
rol al,1 ; put 3 bits at bottom
and al,7
mov dl,al ; save in dl
mov al,dh ; get ms 8 bits,
ror al,1
ror al,1 ; but at bottom
and al,7
mov cl,al ; save in cl
mov al,1
rol al,cl ; set the correct bit,
mov di,offset mcast_list_bits
mov dh,0
add di,dx
or cs:[di],al
pop di
pop cx
ret
;
; dx is high,
; bx is low.
; al is data
upd_crc:
push cx
mov cx,8 ; do 8 bits
mov ah,0
upd_crc1:
shl bx,1 ; shift bx
rcl dx,1 ; through dx
rcl ah,1 ; carry is at bottom of ah
xor ah,al ; xor with lsb of data
rcr ah,1 ; and put in carry bit
jnc upd_crc2
;
; autodin is x^32+x^26+x^23x^22+x^16+
; x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+1
xor dx,0000010011000001b
xor bx,0001110110110111b
upd_crc2:
shr al,1 ; shift the data
loop upd_crc1
pop cx
ret
; Set the multicast filter mask bits in case promiscuous rcv wanted
set_8390_multi:
push cs
pop ds
assume ds:code
loadport
setport EN_CCMD ; Chip command register
pause_
mov cx, 8 ; Eight bytes of multicast filter
mov si, offset mcast_list_bits ; Where bits are, if not all ones
cli ; Protect from irq changing page bits
mov al, ENC_NODMA+ENC_PAGE1+ENC_STOP
out dx, al ; Switch to page one for writing eaddr
setport EN1_MULT ; Where it goes in 8390
pause_
mov al, mcast_all_flag ; Want all ones or just selected bits?
or al, al
je set_mcast_2 ; Just selected ones
mov al, 0ffh ; Ones for filter
set_mcast_all:
out dx, al ; Write a mask byte
inc dl ; Step to next one
loop set_mcast_all ; ..
jmp short set_mcast_x
set_mcast_2:
lodsb ; Get a byte of mask bits
out dx, al ; Write a mask byte
inc dl ; Step to next I/O register
loop set_mcast_2 ; ..
set_mcast_x:
loadport
setport EN_CCMD ; Chip command register
pause_
mov al, ENC_NODMA+ENC_PAGE0+ENC_START
out dx, al ; Restore to page zero
sti ; OK for interrupts now
ret
public reset_board
reset_board:
assume ds:nothing
reset_8390
setport EN_CCMD ; Chip command reg
pause_
mov al, ENC_STOP+ENC_NODMA
out dx, al ; Stop the DS8390
setport EN0_ISR
mov ax,18 ;half a second ought be to enuf.
call set_timeout
reset_board_loop:
pause_
in al,dx ; get isr
and al,ENISR_RESET
jnz reset_board_done
call do_timeout
jne reset_board_loop
reset_board_done:
ret
public terminate
terminate:
terminate_board
ret
public reset_interface
reset_interface:
assume ds:code
call reset_board
loadport ; Base of I/O regs
setport EN0_ISR ; Interrupt status reg
pause_
mov al, 0ffh ; Clear all pending interrupts
out dx, al ; ..
setport EN0_IMR ; Interrupt mask reg
pause_
xor al, al ; Turn off all enables
out dx, al ; ..
ret
; Linkages to non-device-specific routines
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type, dl = packet class.
;It returns with es:di = 0 if don't want this type or if no buffer available.
extrn recv_find: near
;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
extrn recv_copy: near
extrn count_in_err: near
extrn count_out_err: near
public recv
recv:
;called from the recv isr. All registers have been saved, and ds=cs.
;Actually, not just receive, but all interrupts come here.
;Upon exit, the interrupt will be acknowledged.
;ne1000 and ne2000 routines are identical to this point (except that ne2000
; masks off interrupts).
assume ds:code
mkle LE_RECV_E, 0, 0, 0
check_isr: ; Was there an interrupt from this card?
loadport ; Point at card's I/O port base
ram_enable
setport EN0_IMR ; point at interrupt masks
pause_ ; switch off, this way we can
mov al,0 ; leave the chip running.
out dx,al ; no interrupts please.
setport EN0_ISR ; Point at interrupt status register
pause_
in al, dx ; Get pending interrupts
and al, ENISR_ALL ; Any?
jnz isr_test_overrun
mkle LE_RECV_X, 0, 0, 0
jmp interrupt_done ; Go if none
; First, a messy procedure for handling the case where the rcvr
; over-runs its ring buffer. This is spec'ed by National for the chip.
; This is handled differently in sample code from 3Com and from WD.
; This is close to the WD version. May need tweaking if it doesn't
; work for the 3Com card.
isr_test_overrun:
test al,ENISR_OVER ; Was there an overrun?
jnz recv_overrun ; Go if so.
jmp recv_no_overrun ; Go if not.
recv_overrun:
setport EN_CCMD ; Stop the chip
pause_
mov al, ENC_STOP+ENC_NODMA
out dx, al ; Write "stop" to command register
mov al, ENC_NODMA+ENC_PAGE1 ; Could be in previous out, but
out dx,al ; was only tested this way
setport EN1_CURPAG ; Get current page
in al,dx
mov bl,al ; save it
setport EN_CCMD ;
mov al, ENC_NODMA+ENC_PAGE0
out dx,al ; Back to page 0
; Remove one frame from the ring
setport EN0_BOUNDARY ; Find end of this frame
pause_
in al, dx ; Get memory page number
inc al ; Page plus 1
cmp al, sm_rstop_ptr ; Wrapped around ring?
jnz rcv_ovr_nwrap ; Go if not
mov al, SM_RSTART_PG ; Yes, wrap the page pointer
rcv_ovr_nwrap:
cmp al,bl ; Check if buffer emptry
je rcv_ovr_empty ; Yes ? Don't receive anything
;ne1000 and ne2000 routines are identical to this point (except that ne2000
; masks off interrupts).
mov ah,al ; make a byte address. e.g. page
mov bl,ah ; and save in bl
mov al,0 ; 46h becomes 4600h into buffer
mov cx,RCV_HDR_SIZE ; size of rcv_hdr
mov di,offset rcv_hdr ;point to header
push ds
pop es ; set es to right place
call block_input
mov al, rcv_hdr+EN_RBUF_STAT ; Get the buffer status byte
test al,ENRSR_RXOK ; Is this frame any good?
jz rcv_ovr_ng ; Skip if not
call rcv_frm ; Yes, go accept it
rcv_ovr_ng:
mov al,rcv_hdr+EN_RBUF_NXT_PG ; Get pointer to next frame
dec al ; Back up one page
cmp al,SM_RSTART_PG ; Did it wrap?
jae rcv_ovr_nwr2
mov al,sm_rstop_ptr ; Yes, back to end of ring
dec al
rcv_ovr_nwr2:
loadport ; Point at boundary reg
setport EN0_BOUNDARY ; ..
pause_
out dx, al ; Set the boundary
rcv_ovr_empty:
loadport ; Point at boundary reg
setport EN0_RCNTLO ; Point at byte count regs
pause_
xor al, al ; Clear them
out dx, al ; ..
setport EN0_RCNTHI
pause_
out dx, al
setport EN0_ISR ; Point at status reg
pause_
mov cx, 8000h ; Timeout counter
rcv_ovr_rst_loop:
in al, dx ; Is it finished resetting?
test al,ENISR_RESET ; ..
jmp $+2 ; limit chip access rate
loopnz rcv_ovr_rst_loop; Loop til reset, or til timeout
loadport ; Point at Transmit control reg
setport EN0_TXCR ; ..
pause_
mov al, ENTXCR_LOOP ; Put transmitter in loopback mode
out dx, al ; ..
setport EN_CCMD ; Point at Chip command reg
pause_
mov al, ENC_START+ENC_NODMA
out dx, al ; Start the chip running again
setport EN0_TXCR ; Back to TX control reg
pause_
xor al, al ; Clear the loopback bit
out dx, al ; ..
setport EN0_ISR ; Point at Interrupt status register
pause_
mov al, ENISR_OVER ; Clear the overrun interrupt bit
out dx, al ; ..
call count_in_err ; Count the anomaly
jmp check_isr ; Done with the overrun case
recv_no_overrun:
; Handle receive flags, normal and with error (but not overrun).
test al,ENISR_RX+ENISR_RX_ERR ; Frame received without overrun?
jnz recv_frame ; Go if so.
jmp recv_no_frame ; Go if not.
recv_frame:
loadport ; Point at Chip's Command Reg
setport EN0_ISR ; Point at Interrupt status register
pause_
mov al, ENISR_RX+ENISR_RX_ERR
out dx, al ; Clear those requests
setport EN_CCMD ; ..
pause_
mov al, ENC_NODMA+ENC_PAGE1+ENC_START
out dx, al ; Switch to page 1 registers
setport EN1_CURPAG ;Get current page of rcv ring
pause_
in al, dx ; ..
mov ah, al ; Hold current page in AH
setport EN_CCMD ; Back to page zero registers
pause_
mov al, ENC_NODMA+ENC_PAGE0+ENC_START
out dx, al ; Switch back to page 0 registers
setport EN0_BOUNDARY ;Get boundary page
pause_
in al, dx ; ..
inc al ; Step boundary from last used page
cmp al, sm_rstop_ptr ; Wrap if needed
jne rx_nwrap3 ; Go if not
mov al, SM_RSTART_PG ; Wrap to first RX page
rx_nwrap3:
cmp al, ah ; Read all the frames?
je recv_frame_break ; Finished them all
mov ah,al ; make a byte address. E.G. page
mov al,0 ; 46h becomes 4600h into buffer
mov bl,ah
mov cx,RCV_HDR_SIZE
mov di,offset rcv_hdr
push ds
pop es ; set es to right place
call block_input
mov al, rcv_hdr+EN_RBUF_STAT ; Get the buffer status byte
test al,ENRSR_RXOK ; Good frame?
jz recv_err_no_rcv
call rcv_frm ; Yes, go accept it
jmp recv_no_rcv
recv_err_no_rcv:
or byte ptr soft_rx_err_bits,al
add word ptr soft_rx_errors,1
adc word ptr soft_rx_errors+2,0
recv_no_rcv:
mov al, rcv_hdr+EN_RBUF_NXT_PG ; Start of next frame
dec al ; Make previous page for new boundary
cmp al, SM_RSTART_PG ; Wrap around the bottom?
jge rcv_nwrap4
mov al, sm_rstop_ptr ; Yes
dec al
rcv_nwrap4:
loadport ; Point at the Boundary Reg again
setport EN0_BOUNDARY ; ..
pause_
out dx, al ; Set new boundary
jmp recv_frame ; See if any more frames
recv_frame_break:
loadport ; Point at Command register
setport EN_CCMD ; ..
pause_
mov al, ENC_NODMA+ENC_PAGE0+ENC_START
out dx,al
jmp check_isr ; See if any other interrupts pending
recv_no_frame: ; Handle transmit flags.
test al,ENISR_TX+ENISR_TX_ERR ; Frame transmitted?
jnz isr_tx ; Go if so.
jmp isr_no_tx ; Go if not.
isr_tx:
mov ah, al ; Hold interrupt status bits
loadport ; Point at Transmit Status Reg
setport EN0_TSR ; ..
pause_
in al, dx ; ..
test ah,ENISR_TX ; Non-error TX?
jz isr_tx_err ; No, do TX error completion
call count_soft_err ; soft error ??
test al,ENTSR_COLL16 ; Jammed for 16 transmit tries?
jz isr_tx_njam ; Go if not
call count_out_err ; Yes, count those
isr_tx_njam:
setport EN0_ISR ; Clear the TX complete flag
pause_
mov al, ENISR_TX ; ..
out dx, al ; ..
jmp isr_tx_done
isr_tx_err:
test al,ENTSR_FU ; FIFO Underrun?
jz isr_txerr_nfu
call count_out_err ; Yes, count those
isr_txerr_nfu:
loadport ; Clear the TX error completion flag
setport EN0_ISR ; ..
pause_
mov al, ENISR_TX_ERR ; ..
out dx, al ; ..
isr_tx_done:
; If TX queue and/or TX shared memory ring buffer were being
; used, logic to step through them would go here. However,
; in this version, we just clear the flags for background to notice.
jmp check_isr ; See if any other interrupts on
isr_no_tx:
; Now check to see if any counters are getting full
test al,ENISR_COUNTERS ; Interrupt to handle counters?
jnz isr_stat ; Go if so.
jmp isr_no_stat ; Go if not.
isr_stat:
; We have to read the counters to clear them and to clear the interrupt.
; Version 1 of the PC/FTP driver spec doesn't give us
; anything useful to do with the data, though.
; Fix this up for V2 one of these days.
loadport ; Point at first counter
setport EN0_COUNTER0 ; ..
pause_
in al, dx ; Read the count, ignore it.
setport EN0_COUNTER1
pause_
in al, dx ; Read the count, ignore it.
setport EN0_COUNTER2
pause_
in al, dx ; Read the count, ignore it.
setport EN0_ISR ; Clear the statistics completion flag
pause_
mov al, ENISR_COUNTERS ; ..
out dx, al ; ..
isr_no_stat:
jmp check_isr ; Anything else to do?
interrupt_done:
ret
; Do the work of copying out a receive frame.
; Called with bl/ the page number of the frame header in shared memory
public rcv_frm
rcv_frm:
mkle LE_RCVFRM_E, 0, 0, 0
; first do a software multicast filter.
push bx ; save page.
cmp mcast_sw_filter,1 ; do software check of mcast ?
jnz rcv_frm_ok ; no, accept.
mov ax,word ptr rcv_hdr+EN_RBUF_NHDR ; get first word of address
test al,1 ; odd first byte
jz rcv_frm_ok ; must be our address if even
inc word ptr mcast_sw_fin
mov bx,word ptr rcv_hdr+EN_RBUF_NHDR+2 ; get second word of address
mov dx,word ptr rcv_hdr+EN_RBUF_NHDR+4 ; get third word of address
mov di,offset mcast_tab_b ; point to table and broadcast
mov cx,mcast_hcount ; get number in table
inc cx ; plus the broadcast
rcv_loop:
mov si,di ; save this table entry
cmp ax,[di]
jnz rcv_trynext
inc di
inc di
cmp bx,[di]
jnz rcv_trynext
inc di
inc di
cmp dx,[di]
jz rcv_mc_ok ; got it.
rcv_trynext:
mov di,si ; get table back,
add di,6
loop rcv_loop
pop bx ; restore bx
jmp rcv_no_copy
rcv_mc_ok:
inc word ptr mcast_sw_fout
rcv_frm_ok:
; Set cx to length of this frame.
mov ch, rcv_hdr+EN_RBUF_SIZE_HI ; Extract size of frame
mov cl, rcv_hdr+EN_RBUF_SIZE_LO ; Extract size of frame
sub cx, EN_RBUF_NHDR ; Less the header stuff
; Set es:di to point to Ethernet type field.
mov di, offset rcv_hdr+EN_RBUF_NHDR+EADDR_LEN+EADDR_LEN
push cx ; Save frame size
push es
mov ax, cs ; Set ds = code
mov ds, ax
mov es,ax
assume ds:code
mov dl, BLUEBOOK ;assume bluebook Ethernet.
mov ax, es:[di]
xchg ah, al
cmp ax, 1500
ja BlueBookPacket
inc di ;set di to 802.2 header
inc di
mov dl, IEEE8023
BlueBookPacket:
call recv_find ; See if type and size are wanted
pop ds ; RX page pointer in ds now
assume ds:nothing
pop cx
pop bx
cld ; Copies below are forward, please
mov ax, es ; Did recv_find give us a null pointer?
or ax, di ; ..
je rcv_no_copy ; If null, don't copy the data
push cx ; We will want the count and pointer
push es ; to hand to client after copying,
push di ; so save them at this point
mov ah,bl ; set ax to page to start from
mov al,EN_RBUF_NHDR ; skip the header stuff
call block_input
pop si ; Recover pointer to destination
pop ds ; Tell client it's his source
pop cx ; And it's this long
assume ds:nothing
call recv_copy ; Give it to him
rcv_no_copy:
push cs ; Put ds back in code space
pop ds ; ..
assume ds:code
mkle LE_RCVFRM_X, 0, 0, 0
ret ; That's it for rcv_frm
public recv_exiting
recv_exiting:
;called from the recv isr after interrupts have been acknowledged.
;Only ds and ax have been saved.
assume ds:nothing
push dx
loadport
setport EN0_IMR ; Tell card it can cause these interrupts
pause_
mov al, ENISR_ALL
out dx, al
pop dx
ret
include timeout.asm
;any code after this will not be kept after initialization.
end_resident label byte
using_186_msg db "Using 80[123]86 I/O instructions.",CR,LF,'$'
;standard EN0_DCFG contents:
endcfg db 048h ; Set burst mode, 8 deep FIFO
; Called once to initialize the card
public etopen
etopen: ; Initialize interface
;Determine the processor type. The 8088 and 8086 will actually shift ax
;over by 33 bits, while the 80[123]86 use a shift count mod 32.
;This bit lifted from NI5010 driver.
mov cl,33
mov ax,0ffffh
shl ax,cl
jz not_186
mov is_186,1
mov dx,offset using_186_msg
mov ah,9
int 21h
not_186:
;Step 1. Reset and stop the 8390.
call reset_board
;Step 2. Init the Data Config Reg.
loadport
mov al,endcfg
setport EN0_DCFG
pause_
out dx,al
;Step 3. Clear Remote Byte Count Regs.
mov al, 0
setport EN0_RCNTLO
pause_
out dx,al
setport EN0_RCNTHI
pause_
out dx,al
;Step 4. Set receiver to monitor mode
mov al, ENRXCR_MON
setport EN0_RXCR
pause_
out dx,al
;Step 5. Place NIC into Loopback Mode 1.
mov al,ENTXCR_LOOP
setport EN0_TXCR
pause_
out dx,al
;Step 6. Do anything special that the card needs.
call init_card
;Step 7. Re-init endcfg in case they put it into word mode.
loadport
mov al,endcfg
setport EN0_DCFG
pause_
out dx,al
;Step 8. Init EN0_STARTPG to same value as EN0_BOUNDARY
loadport
mov al,SM_RSTART_PG
setport EN0_STARTPG
pause_
out dx,al
mov al,SM_RSTART_PG
setport EN0_BOUNDARY
pause_
out dx,al
mov al,sm_rstop_ptr
setport EN0_STOPPG
pause_
out dx,al
;Step 9. Write 1's to all bits of EN0_ISR to clear pending interrupts.
mov al, 0ffh
setport EN0_ISR
pause_
out dx,al
;Step 10. Init EN0_IMR as desired.
mov al, ENISR_ALL
setport EN0_IMR
pause_
out dx,al
;Step 11. Init the Ethernet address and multicast filters.
call set_8390_eaddr ; Now set the address in the 8390 chip
call set_8390_multi ; Put the right stuff into 8390's multicast masks
;Step 12. Program EN_CCMD for page 1.
loadport
mov al, ENC_PAGE1 + ENC_NODMA + ENC_STOP
setport EN_CCMD
pause_
out dx,al
;Step 13. Program the Current Page Register to same value as Boundary Pointer.
mov al,SM_RSTART_PG
setport EN1_CURPAG
pause_
out dx,al
;Step 14. Program EN_CCMD back to page 0, and start it.
mov al, ENC_NODMA + ENC_START
setport EN_CCMD
pause_
out dx,al
mov al, 0 ;set transmitter mode to normal.
setport EN0_TXCR
pause_
out dx,al
call set_recv_isr ; Put ourselves in interrupt chain
loadport
setport EN0_RXCR ; Tell it what frames to accept
pause_
mov al, rxcr_bits ; As most recently set by set_mode
out dx, al
ram_enable
mov al, int_no ; Get board's interrupt vector
add al, 8
cmp al, 8+8 ; Is it a slave 8259 interrupt?
jb set_int_num ; No.
add al, 70h - 8 - 8 ; Map it to the real interrupt.
set_int_num:
xor ah, ah ; Clear high byte
mov int_num, ax ; Set parameter_list int num.
mov dx, offset end_resident ; Report our size
clc ; Say no error
ret ; Back to common code